#include "express.h"

namespace libxcom {
Express::Express(const OpItem &op) : opItem(op)
{
    children.clear();
}

Express::~Express()
{
    children.clear();
}

void Express::Insert(std::shared_ptr<Express> expr)
{
    if (expr == nullptr) {
        HTELINK_LOG_ERR("null expr");
        return;
    }
    children.insert(children.begin(), expr);
}

void Express::Add(std::shared_ptr<Express> expr)
{
    if (!expr) {
        HTELINK_LOG_ERR("null expr");
        return;
    }
    children.push_back(expr);
}

std::shared_ptr<Express> Express::ChildAt(int index)
{
    if (index < children.size()) {
        return children.at(index);
    }
    return NULL;
}

int32_t Express::ChildrenSize()
{
    return children.size();
}

OP_TYPE Express::Type()
{
    return opItem.type;
}

OpItem &Express::Value()
{
    return opItem;
}

std::string Express::VarName()
{
    if (opItem.op.var[0] == '$' || opItem.op.var[0] == '&')
        return &opItem.op.var[1];
    else
        return opItem.op.var;
}

char Express::VarPrefix()
{
    return opItem.op.var[0];
}

std::string Express::exprStr()
{
    if (Type() == OP_NUM) {
        return opItem.showStr();
    } else if (Type() == OP_BASE) {
        std::shared_ptr<Express> child0 = ChildAt(0);
        std::shared_ptr<Express> child1 = ChildAt(1);

        if (ChildrenSize() == 2) {
            if (child0 == nullptr || child1 == nullptr) {
                return "null" + opItem.showStr() + "null";
            }
            std::string str;
            if (opItem.priority() > child0->opItem.priority()) {
                str += "(" + child0->exprStr() + ")";
            } else
                str += child0->exprStr();
            str += opItem.showStr();
            if (opItem.priority() > child1->opItem.priority()) {
                str += "(" + child1->exprStr() + ")";
            } else
                str += child1->exprStr();
            return str;
        } else if (ChildrenSize() == 1) {
            if (child0 == nullptr) {
                return opItem.showStr() + "null";
            }
            std::string str = opItem.showStr();
            if (opItem.priority() > child0->opItem.priority()) {
                str += "(" + child0->exprStr() + ")";
            } else
                str += child0->exprStr();
            return str;
        } else {
            return opItem.showStr();
        }
    } else if (Type() == OP_VAR) {
        return opItem.showStr();
    } else if (Type() == OP_FUNC || Type() == OP_KEYFUNC) {
        std::string str = opItem.showStr();
        if (ChildrenSize() == 0)
            return str;
        str += "(";
        for (int i = 0; i < children.size(); i++) {
            if (i != 0)
                str += ",";
            str += children.at(i)->exprStr();
        }
        str += ")";
        return str;
    } else if (Type() == OP_BRACKET || Type() == OP_BRACE) {
        std::string str = opItem.leftStr();
        for (int num = 0; num < ChildrenSize(); num++) {
            if (num != 0) {
                if (Type() == OP_BRACKET)
                    str += " ";
                else
                    str += ",";
            }
            std::shared_ptr<Express> childk = ChildAt(num);
            if (childk == nullptr) {
                str += "null";
            } else
                str += childk->exprStr();
        }
        return str += opItem.rightStr();
    } else {
        return "null";
    }
}

std::string Express::funcName()
{
    // 关键函数,ditch,fill)
    return opItem.showStr();
}

OpValue Express::GenCmd(Operator &iOperator)
{
    return ParseCmd(iOperator);
}

OpValue Express::ParseCmd(Operator &iOperator, int32_t pos, const CmdValue &recvCmd)
{
    if (Type() == OP_NUM) {
        return Value().digit;
    } else if (Type() == OP_BASE) {
        OpValue v1;
        OpValue v2;
        std::shared_ptr<Express> child0 = ChildAt(0);
        std::shared_ptr<Express> child1 = ChildAt(1);

        do {
            if (ChildrenSize() == 2) {
                if (child0 == nullptr || child1 == nullptr) {
                    HTELINK_LOG_ERR("child 0 & 1 has been deleted");
                    return OpValue::Null();
                }
                v1 = child0->ParseCmd(iOperator, pos);
                v2 = child1->ParseCmd(iOperator, pos);
                return iOperator.BaseCall(Value().op.ch, v1, v2);
            } else if (ChildrenSize() == 1) {
                if (child0 == nullptr) {
                    HTELINK_LOG_ERR("child 0 has been deleted");
                    return OpValue::Null();
                }
                v1 = 0;
                v2 = child0->ParseCmd(iOperator, pos);
                return iOperator.SingleCall(Value().op.ch, v2);
            } else {
                HTELINK_LOG_ERR("%c:error params count", Value().op.ch);
                return OpValue::Null();
            }
        } while (0);
    } else if (Type() == OP_VAR) {
        if (VarPrefix() == '&')
            return VarName().c_str();
        else if (utils::isInteger(VarName().c_str())) {
            int32_t v = atoi(VarName().c_str());
            if (v == 0)
                return iOperator.GetParam(0);
            return iOperator.GetParam(v - 1);
        } else {
            std::shared_ptr<Express> expr = iOperator.FindDefinition(VarName());
            if (expr != nullptr) {
                return expr->ParseCmd(iOperator, pos, recvCmd);
            } else {
                return iOperator.VarCall(VarName().c_str(), pos, recvCmd);
            }
        }

    } else if (Type() == OP_FUNC) {
        Operator::AutoOperator autoOp(iOperator, Operator::AUTO_PARAM, 1);
        for (int32_t i = 0; i < ChildrenSize(); i++) {
            OpValue value;
            std::shared_ptr<Express> childi = ChildAt(i);
            if (childi == nullptr) {
                HTELINK_LOG_ERR("child %d has been deleted", i);
                return OpValue::Null();
            }
            if (childi->Type() != OP_KEYFUNC)
                value = childi->ParseCmd(iOperator, pos);
            else
                value = childi->ParseCmd(iOperator, pos, recvCmd);
            if (value.IsEmpty()) {
                HTELINK_LOG_DEBUG("func %s:param %d caculate error", VarName().c_str(), i);
                return value;
            }
            autoOp.AddList(value);
        }

        autoOp.StackAll();

        std::shared_ptr<Express> expr = iOperator.FindDefinition(VarName());
        if (expr != nullptr) {
            return expr->ParseCmd(iOperator, pos, recvCmd);
        } else
            return iOperator.FunctionCall(VarName().c_str(), pos, recvCmd);
    } else if (Type() == OP_KEYFUNC) {
        if (ChildrenSize() < 1) {
            HTELINK_LOG_ERR("keyfunc parameter is less");
            return OpValue::Null();
        }

        OpValue v1;
        std::shared_ptr<Express> child0 = ChildAt(0);
        std::shared_ptr<Express> child1 = ChildAt(1);

        if (child0 == nullptr) {
            HTELINK_LOG_ERR("child 0 has been deleted");
            return OpValue::Null();
        }
        v1 = child0->ParseCmd(iOperator, pos);

        if (!v1.IsInteger()) {
            HTELINK_LOG_ERR("ditch first parameter must be a integer");
            return OpValue::Null();
        }

        Operator::AutoOperator autoOp(iOperator, Operator::AUTO_PARAM);
        if (!strcmp(VarName().c_str(), "ditch")) {
            OpValue get = iOperator.KeyFunctionCall(VarName().c_str(), v1.Integer(), recvCmd);
            iOperator.AddParam(get);
            if (ChildrenSize() > 1) {
                if (child1 == nullptr) {
                    HTELINK_LOG_ERR("child 1 has been deleted");
                    return OpValue::Null();
                }
                child1->ParseCmd(iOperator, pos, recvCmd);
            }
            return get;
        } else if (!strcmp(VarName().c_str(), "fill")) {
            OpValue fill;
            if (ChildrenSize() > 1) {
                if (child1 == nullptr) {
                    HTELINK_LOG_ERR("child 1 has been deleted");
                    return OpValue::Null();
                }
                fill = child1->ParseCmd(iOperator, pos, recvCmd);
            }
            iOperator.AddParam(fill);
            if (ChildrenSize() >= 3) {
                std::shared_ptr<Express> child2 = ChildAt(2);
                if (child2 == nullptr) {
                    HTELINK_LOG_ERR("child 2 has been deleted");
                    return OpValue::Null();
                }
                OpValue v2 = child2->ParseCmd(iOperator, pos);
                if (v2.IsInteger())
                    iOperator.AddParam(v2);
            }
            return iOperator.KeyFunctionCall(VarName().c_str(), v1.Integer(), recvCmd);
        }

        return OpValue::Null();
    } else if (Type() == OP_BRACKET) {
        int32_t start = 0;
        Operator::AutoOperator autoOp(iOperator, Operator::AUTO_CMD);
        CmdValue recv = recvCmd;
        bool exit = false;
        /*
         * [] 表达式逆向运算
         *
         */
        for (int num = 0; num < ChildrenSize(); num++) {
            OpValue value;
            std::shared_ptr<Express> childk = ChildAt(num);
            if (childk == nullptr) {
                HTELINK_LOG_ERR("child %d has been deleted", num);
                return OpValue::Null();
            }

            value = childk->ParseCmd(iOperator, start, recv);

            if (value.IsEmpty()) {
                HTELINK_LOG_DEBUG("[]:param %d caculate error, type: %d", num, childk->Type());
                return OpValue::Null();
            }

            /* [] 逆向计算 */
            if (!recv.IsEmpty()) {
                CmdValue cmdValue = value.GenCmd();
                int32_t shift = recv.Contains(cmdValue);

                if (shift == -1) {
                    HTELINK_LOG_DEBUG("[]:param %d not match", num);
                    return OpValue::Null();
                }
                if (num != 0) {
                    if (shift == cmdValue.Length())
                        recv.TrimLeft(shift);
                } else
                    recv.TrimLeft(shift);
                if (recv.IsEmpty())
                    exit = true;
            }
            start += iOperator.InsertCmd(num, value);
            if (exit)
                break;
        }
        OpValue result = iOperator.GetCmd();
        return result;
    } else if (Type() == OP_BRACE) {
        OpValue result;
        for (int32_t i = 0; i < ChildrenSize(); i++) {
            OpValue value;
            std::shared_ptr<Express> childk = ChildAt(i);

            if (childk == nullptr) {
                HTELINK_LOG_ERR("child %d has been deleted", i);
                return OpValue::Null();
            }

            value = childk->ParseCmd(iOperator, pos, recvCmd);
            if (value.IsEmpty()) {
                HTELINK_LOG_DEBUG("func %s:param %d caculate error", VarName().c_str(), i);
                return result;
            }
            if (result.IsEmpty())
                result = value;
            else {
                // result.AllocNext(value);
            }
        }
        return result;
    } else {
        HTELINK_LOG_ERR("error operator %u", Type());
        return OpValue::Null();
    }
}

TYPE_DEFINE Express::ResultType(Operator &op)
{
    if (Type() == OP_NUM) {
        return Value().digit.type;
    } else if (Type() == OP_BASE) {
        std::shared_ptr<Express> child0 = ChildAt(0);
        std::shared_ptr<Express> child1 = ChildAt(1);

        TYPE_DEFINE t1 = child0 == nullptr ? child0->ResultType(op) : TYPE_S32;
        TYPE_DEFINE t2 = child1 == nullptr ? child1->ResultType(op) : TYPE_S32;
        return t1 > t2 ? t1 : t2;
    } else if (Type() == OP_VAR) {
        if (VarPrefix() == '&')
            return TYPE_S32;
        else if (utils::isInteger(VarName().c_str())) {
            return TYPE_CMD;
        } else {
            return op.VarType(VarName().c_str());
        }
    } else if (Type() == OP_FUNC) {
        return op.FuncType(VarName().c_str());
    } else if (Type() == OP_KEYFUNC) {

        return TYPE_CMD;
    } else if (Type() == OP_BRACKET) {
        return TYPE_CMD;
    } else if (Type() == OP_BRACE) {
        return TYPE_CMD;
    } else {
        HTELINK_LOG_ERR("error operator %u", Type());
        return TYPE_NONE;
    }
}
} // namespace libxcom
